// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { HmacSha1, Message, Sha1 } from "./sha1.ts";
import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts";

const moduleDir = dirname(fromFileUrl(import.meta.url));
const testdataDir = resolve(moduleDir, "testdata");

/** Handy function to convert an array/array buffer to a string of hex values. */
function toHexString(value: number[] | ArrayBuffer): string {
  const array = new Uint8Array(value);
  let hex = "";
  for (const v of array) {
    const c = v.toString(16);
    hex += c.length === 1 ? `0${c}` : c;
  }
  return hex;
}

const fixtures: {
  sha1: Record<string, Record<string, Message>>;
  sha1Hmac: Record<string, Record<string, [Message, Message]>>;
} = {
  sha1: {
    "ascii": {
      "da39a3ee5e6b4b0d3255bfef95601890afd80709": "",
      "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12":
        "The quick brown fox jumps over the lazy dog",
      "408d94384216f890ff7a0c3528e8bed1e0b01621":
        "The quick brown fox jumps over the lazy dog.",
    },
    "ascii more than 64 bytes": {
      "8690faab7755408a03875895176fac318f14a699":
        "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity.",
    },
    "UTF8": {
      "7be2d2d20c106eee0836c9bc2b939890a78e8fb3": "中文",
      "9e4e5d978deced901d621475b03f1ded19e945bf": "aécio",
      "4667688a63420661469c8dbc0f871770349bab08": "𠜎",
    },
    "UTF8 more than 64 bytes": {
      "ad8aae581c915fe01c4964a5e8b322cae74ee5c5":
        "訊息摘要演算法第五版（英語：Message-Digest Algorithm 5，縮寫為MD5），是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一",
      "3a15ad3ce9efdd4bf982eaaaecdeda36a887a3f9":
        "訊息摘要演算法第五版（英語：Message-Digest Algorithm 5，縮寫為MD5），是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一（又譯雜湊演算法、摘要演算法等），主流程式語言普遍已有MD5的實作。",
    },
    "special length": {
      "4cdeae78e8b7285aef73e0a15eec7d5b30f3f3e3":
        "0123456780123456780123456780123456780123456780123456780",
      "e657e6bb6b5d0c2bf7e929451c14a5302589a60b":
        "01234567801234567801234567801234567801234567801234567801",
      "e7ad97591c1a99d54d80751d341899769884c75a":
        "0123456780123456780123456780123456780123456780123456780123456780",
      "55a13698cdc010c0d16dab2f7dc10f43a713f12f":
        "01234567801234567801234567801234567801234567801234567801234567801234567",
      "006575418c27b0158e55a6d261c46f86b33a496a":
        "012345678012345678012345678012345678012345678012345678012345678012345678",
    },
    "Array": {
      "da39a3ee5e6b4b0d3255bfef95601890afd80709": [],
      "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12": [
        84,
        104,
        101,
        32,
        113,
        117,
        105,
        99,
        107,
        32,
        98,
        114,
        111,
        119,
        110,
        32,
        102,
        111,
        120,
        32,
        106,
        117,
        109,
        112,
        115,
        32,
        111,
        118,
        101,
        114,
        32,
        116,
        104,
        101,
        32,
        108,
        97,
        122,
        121,
        32,
        100,
        111,
        103,
      ],
      "55a13698cdc010c0d16dab2f7dc10f43a713f12f": [
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
      ],
    },
    "Uint8Array": {
      "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12": new Uint8Array([
        84,
        104,
        101,
        32,
        113,
        117,
        105,
        99,
        107,
        32,
        98,
        114,
        111,
        119,
        110,
        32,
        102,
        111,
        120,
        32,
        106,
        117,
        109,
        112,
        115,
        32,
        111,
        118,
        101,
        114,
        32,
        116,
        104,
        101,
        32,
        108,
        97,
        122,
        121,
        32,
        100,
        111,
        103,
      ]),
    },
    "Int8Array": {
      "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12": new Int8Array([
        84,
        104,
        101,
        32,
        113,
        117,
        105,
        99,
        107,
        32,
        98,
        114,
        111,
        119,
        110,
        32,
        102,
        111,
        120,
        32,
        106,
        117,
        109,
        112,
        115,
        32,
        111,
        118,
        101,
        114,
        32,
        116,
        104,
        101,
        32,
        108,
        97,
        122,
        121,
        32,
        100,
        111,
        103,
      ]),
    },
    "ArrayBuffer": {
      "5ba93c9db0cff93f52b521d7420e43f6eda2784f": new ArrayBuffer(1),
    },
  },
  sha1Hmac: {
    "Test Vectors": {
      "b617318655057264e28bc0b6fb378c8ef146be00": [
        [
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
          0x0b,
        ],
        "Hi There",
      ],
      "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79": [
        "Jefe",
        "what do ya want for nothing?",
      ],
      "125d7342b9ac11cd91a39af48aa17b4f63f175d3": [
        [
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
        ],
        [
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
          0xdd,
        ],
      ],
      "4c9007f4026250c6bc8414f9bf50c86c2d7235da": [
        [
          0x01,
          0x02,
          0x03,
          0x04,
          0x05,
          0x06,
          0x07,
          0x08,
          0x09,
          0x0a,
          0x0b,
          0x0c,
          0x0d,
          0x0e,
          0x0f,
          0x10,
          0x11,
          0x12,
          0x13,
          0x14,
          0x15,
          0x16,
          0x17,
          0x18,
          0x19,
        ],
        [
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
          0xcd,
        ],
      ],
      "90d0dace1c1bdc957339307803160335bde6df2b": [
        [
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
        ],
        "Test Using Larger Than Block-Size Key - Hash Key First",
      ],
      "217e44bb08b6e06a2d6c30f3cb9f537f97c63356": [
        [
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
          0xaa,
        ],
        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
      ],
    },
    "UTF8": {
      "f495de6d0f1b5681070a024bbaed5b5f42847306": ["中文", "中文"],
      "58891d68487ffebddba5925abedec77a5a578db2": ["aécio", "aécio"],
      "a1816bff2dae324c283aeab564d5edb5170fbada": ["𠜎", "𠜎"],
    },
  },
};

const methods = ["array", "arrayBuffer", "digest", "hex"] as const;

for (const method of methods) {
  for (const [name, tests] of Object.entries(fixtures.sha1)) {
    let i = 1;
    for (const [expected, message] of Object.entries(tests)) {
      Deno.test({
        name: `sha1.${method}() - ${name} - #${i++}`,
        fn() {
          const algorithm = new Sha1();
          algorithm.update(message);
          const actual = method === "hex"
            ? algorithm[method]()
            : toHexString(algorithm[method]());
          assertEquals(actual, expected);
        },
      });
    }
  }
}

for (const method of methods) {
  for (const [name, tests] of Object.entries(fixtures.sha1)) {
    let i = 1;
    for (const [expected, message] of Object.entries(tests)) {
      Deno.test({
        name: `sha1.${method}() - ${name} - #${i++}`,
        fn() {
          const algorithm = new Sha1(true);
          algorithm.update(message);
          const actual = method === "hex"
            ? algorithm[method]()
            : toHexString(algorithm[method]());
          assertEquals(actual, expected);
        },
      });
    }
  }
}

for (const method of methods) {
  for (const [name, tests] of Object.entries(fixtures.sha1Hmac)) {
    let i = 1;
    for (const [expected, [key, message]] of Object.entries(tests)) {
      Deno.test({
        name: `hmacSha1.${method}() - ${name} - #${i++}`,
        fn() {
          const algorithm = new HmacSha1(key);
          algorithm.update(message);
          const actual = method === "hex"
            ? algorithm[method]()
            : toHexString(algorithm[method]());
          assertEquals(actual, expected);
        },
      });
    }
  }
}

Deno.test("[hash/sha1] test Uint8Array from Reader", async () => {
  const data = await Deno.readFile(join(testdataDir, "hashtest"));

  const hash = new Sha1().update(data).hex();
  assertEquals(hash, "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3");
});
